package edu.lambdateacher.expressions;

/*
 LambdaTeacher
 Copyright (C) 2000  Kim Mason
 Copyright (C) 2014  Vitalij Zadneprovskij

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

import java.util.HashSet;
import java.util.Set;

import org.apache.log4j.Logger;

import edu.lambdateacher.PropertiesManager;
//import edu.lambdateacher.awt.ExpressionRepresentation;
//import edu.lambdateacher.awt.MessageDialog;

/**
 * This is the Expression class, which forms the superclass for Lambda
 * Expressions. Subclasses can be made of this class (e.g. Application,
 * Abstraction, etc...), and in this way, a tree representing a Lambda
 * expression can be built up. Instances of Expression shouldn't be created in
 * general use (though there is no reason why you couldn't create an expression
 * if the mad fit took you), as they don't really represent anything, but only
 * act as a basis for subclasses.
 */
public abstract class Expression implements Cloneable {

	private static Logger log = Logger.getLogger(Expression.class);

	/***
	 * if this boolean is true, all bindings above this expression
	 * should be trashed.
	 */
	private boolean parentmodified;

	public Expression() {
		parentmodified = false;
	}

	public final boolean getParentModified() {
		return parentmodified;
	}

	public final void setParentModified(boolean inbool) {
		parentmodified = inbool;
	}
	
	/**
	 * 
	 * @param verbose whether to produce all brackets
	 * @return a string which represents this expression
	 */
	public String stringRepresentation(boolean verbose) {
		return new String("");
	}

	/**
	 * Returns true if it is possible to beta reduce this expression
	 */
	public boolean betaAllowed() {
		return false;
	}

	/**
	 * beta_reduce() reduces this expression and returns the result
	 */
	public Expression betaReduce() {
		return this;
	}

	/**
	 * containsUnbound returns true if this expression contains an unbound
	 * variable which matches the input string.
	 */
	public boolean containsUnbound(String inname) {
		return false;
	}

	/**
	 * returns true if eta reduction allowed on this expression
	 */
	public boolean etaAllowed() {
		return false;
	}

	/**
	 * eta_reduce() reduces this expression and returns a clone of the result.
	 */
	public Expression etaReduce() {
		return this;
	}

	/**
	 * Override the clone method in class Object
	 */
	public Object clone() {
		try {
			Expression toreturn = (Expression) super.clone();
			toreturn.setParentModified(this.getParentModified());
			return toreturn;
		} catch (CloneNotSupportedException e) { // this shouldn't happen, as we
													// are cloneable
			log.error("Error while cloning Expression. This is a bug!.", e);
			return null;
		}
	}

	/**
	 * This method returns true if the current expression is reducable, or any
	 * of its sub expressions are reducable.
	 */
	public boolean containsReducable() {
		return false;
	}

	/**
	 * normal_reduce() attempts to reduce this expression and return the result.
	 */
	public Expression normalReduce() {
		return this;
	}



	/**
	 * This function is for substituting the expression param for the variable
	 * with the name inname in the current expression. Note that it will only
	 * sub in while the variable is un-bound. This automatically does alpha
	 * reduction if required.
	 */
	public Expression nameSubstitute(String inname, Expression param) {
		return this;
	}

	/**
	 * findNormalReducable attempts to find the normal reducable subexpression
	 * of the current Expression.
	 */
	public Expression findNormalReducable() {
		return null;
	}

	/**
	 * findApplicativeReducable attempts to find the applicative reducable
	 * subexpression of the current Expression. If there is none, it returns
	 * null.
	 */
	public Expression findApplicativeReducable() {
		return null;
	}

	/**
	 * findUnboundVars returns a HashSet with the names of all of the unbound
	 * variables in the current expression. The input HashSet (bound_vars)
	 * contains all of the variable names that are bound.
	 */
	public Set findUnboundVars(Set bound_vars) {
		return new HashSet();
	}

	/**
	 * removeAllBindings walks down the expression tree, and removes any
	 * bindings that it finds.
	 */
	public Expression removeAllBindings() {
		return this;
	}

	/**
	 * removeBinding walks down the expression tree, and removes any occurences
	 * of the input parameter (toremove).
	 */
	public Expression removeBinding(Binding toremove) {
		return this;
	}


	@Override
	public String toString() {
		return "Expression [parentmodified=" + parentmodified + "]";
	}
	
	/**
	 * reduceExpression simply searches for inexpr, and if the current
	 * expression is equal to inexpr, it reduces it and returns the result.
	 */
	public Expression reduceExpression(Expression inexpr) {
		if (this == inexpr && PropertiesManager.expdlogon) {
			StringBuffer errorMessage = new StringBuffer();
			errorMessage.append("The expression: ");
			errorMessage.append(this.stringRepresentation(false));
			errorMessage.append("is not reducable.");

		}

		return this;
	}
	
}
